#!/usr/bin/env python

###########################################################################
#  Copyright 2010 Jim Pulokas
#
#  This file is part of focusfun.
#  
#  focusfun is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#  
#  focusfun is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with focusfun.  If not, see <http://www.gnu.org/licenses/>.
###########################################################################

#
# This module is based on another one that I was writing to create a general
# purpose v4l2 class.  I never really finished that, so here I just hacked
# it to print out some info about focusing capabilities.
#

import os
import v4l2
from fcntl import ioctl

capability_fields = ['bus_info', 'capabilities', 'card', 'driver', 'version']
queryctrl_fields = ['default', 'flags', 'id', 'maximum', 'minimum', 'name', 'step', 'type']
queryctrl_flags_short = ['DISABLED', 'GRABBED', 'READ_ONLY', 'UPDATE', 'INACTIVE', 'SLIDER']
queryctrl_flags = ['V4L2_CTRL_FLAG_' + flag for flag in queryctrl_flags_short]

name_cid = {}
cid_name = {}
for key,value in v4l2.__dict__.items():
	if key.startswith('V4L2_CID_'):
		name_cid[key] = value
		cid_name[value] = key

def flags_to_list(flags):
	flaglist = []
	for flag in queryctrl_flags:
		flagint = getattr(v4l2, flag)
		if flagint & flags:
			flaglist.append(flag)
	return flaglist

class MenuType(object):
	def __init__(self, dev, qctrl):
		self.max = qctrl.maximum
		self.choices = {}
		self.ichoices = {}
		for i in range(self.max+1):
			q = v4l2.v4l2_querymenu()
			q.id = qctrl.id
			q.index = i
			ioctl(dev, v4l2.VIDIOC_QUERYMENU, q)
			self.choices[q.name] = i
			self.ichoices[i] = q.name

	def __call__(self, arg):
		'takes name or index, returns index'
		if isinstance(arg, int):
			if 0 <= arg <= self.max:
				return arg
		elif arg in self.choices:
			return self.choices[arg]
		raise ValueError('expected name or index of a menu choice')

	def __str__(self):
		names = [self.ichoices[i] for i in range(self.max+1)]
		return 'Menu%s' % (names,)

class ButtonType(object):
	pass

class Device(object):
	def __init__(self, filename):
		self.dev = os.open(filename, os.O_RDONLY)
		self.controls = self.queryctrl_all()

	def querycap(self):
		cp = v4l2.v4l2_capability()
		ioctl(self.dev, v4l2.VIDIOC_QUERYCAP, cp)
		return cp

	def print_cap(self):
		cp = self.querycap()
		for attr in capability_fields:
			if attr == 'capabilities':
				caps = getattr(cp, attr)
				prefix = 'V4L2_CAP_'
				capslist = []
				for v4l2attr,value in v4l2.__dict__.items():
					if v4l2attr.startswith(prefix):
						if caps & value:
							name = v4l2attr[len(prefix):]
							capslist.append(name)
				print '%s: %s' % (attr, capslist)
			else:
				print '%s: %s' % (attr, getattr(cp, attr))

	def print_control(self, ctrl):
		print ''
		print 'Control: "%s"' % (ctrl['name'],)
		for field in queryctrl_fields:
			print '   %s: %s' % (field, ctrl[field])
		print '   API: %s' % (ctrl['API'],)

	def print_controls(self):
		for ctrl in self.controls:
			if 'focus' in ctrl['name'].lower():
				self.print_control(ctrl)

	def queryctrl(self, id):
		ct = v4l2.v4l2_queryctrl()
		ct.id = id
		ioctl(self.dev, v4l2.VIDIOC_QUERYCTRL, ct)
		return ct

	def queryctrl_all(self):
		qctrl = v4l2.v4l2_queryctrl()
		qctrl.id = v4l2.V4L2_CTRL_FLAG_NEXT_CTRL
		try:
			ioctl(self.dev, v4l2.VIDIOC_QUERYCTRL, qctrl)
		except:
			print 'Device does not support V4L2_CTRL_FLAG_NEXT.  Cannot query all controls.'
			return []

		controls = []
		while True:
			qctrldict = self.queryctrl_to_dict(qctrl)
			controls.append(qctrldict)
			qctrl.id |= v4l2.V4L2_CTRL_FLAG_NEXT_CTRL
			try:
				ioctl(self.dev, v4l2.VIDIOC_QUERYCTRL, qctrl)
			except:
				break
		return controls

	def type_to_pytype(self, qctrl):
		t = qctrl.type
		if t == v4l2.V4L2_CTRL_TYPE_INTEGER:
			pytype = int
		elif t == v4l2.V4L2_CTRL_TYPE_BOOLEAN:
			pytype = bool
		elif t == v4l2.V4L2_CTRL_TYPE_MENU:
			pytype = MenuType(self.dev, qctrl)
		elif t == v4l2.V4L2_CTRL_TYPE_BUTTON:
			pytype = ButtonType()
		else:
			raise ValueError('what to do with control type?: %s' % (t,))
		return pytype
	
	def queryctrl_to_dict(self, qctrl):
		d = {}
		## easy ones
		for field in ('default', 'id', 'minimum', 'maximum', 'name', 'step'):
			d[field] = getattr(qctrl, field)
		## others that need to be pythonified
		d['flags'] = flags_to_list(qctrl.flags)
		d['type'] = self.type_to_pytype(qctrl)
		if qctrl.id in cid_name:
			d['API'] = cid_name[qctrl.id]
		else:
			d['API'] = None
		return d

	def g_ctrl(self, id):
		val = v4l2.v4l2_control()
		val.id = id
		ioctl(self.dev, v4l2.VIDIOC_G_CTRL, val)
		return val.value

	def s_ctrl(self, id, value):
		val = v4l2.v4l2_control()
		val.id = id
		val.value = value
		ioctl(self.dev, v4l2.VIDIOC_S_CTRL, val)

	def get_inputs(self):
		inputs = []
		input = v4l2.v4l2_input()
		index = 0
		input.index = index
		while True:
			try:
				err = ioctl(self.dev, v4l2.VIDIOC_ENUMINPUT, input)
			except:
				break
			inputs.append(input)
			input = v4l2.v4l2_input()
			index += 1
			input.index = index
		return inputs

if __name__ == '__main__':
	import sys
	import time
	try:
		devname = sys.argv[1]
	except:
		devname = '/dev/video0'
	dev = Device(devname)
	dev.print_cap()
	dev.print_controls()
